/*
 * OREKIT-X
 * Copyright 2002-2008 CS Communication & Systemes
 * 
 * Parts of this software package have been licensed to CS
 * Communication & Systemes (CS) under one or more contributor license
 * agreements.  See the NOTICE file distributed with this work for
 * additional information.
 *  
 * This is an experimental copy of OREKIT from www.orekit.org.
 * Please use the original OREKIT from orekit.org for normal work
 * unrelated to this research project.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you
 * may not use this file except in compliance with the License.  You
 * may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 * implied.  See the License for the specific language governing
 * permissions and limitations under the License.
 */
package ore.tle;

import ore.errors.OrekitException;

/** 
 * Methods to compute propagated coordinates with the SGP4 model.  See
 * {@link TLEPropagator#Select}.
 * 
 * This implementation is largely inspired from the paper and source
 * code <a
 * href="http://www.celestrak.com/publications/AIAA/2006-6753/">Revisiting
 * Spacetrack Report #3</a> and is fully compliant with its results
 * and tests cases.
 * 
 * @see TLEPropagator#Select
 * 
 * @author Felix R. Hoots, Ronald L. Roehrich, December 1980 (original fortran)
 * @author David A. Vallado, Paul Crawford, Richard Hujsak, T.S. Kelso (C++ translation and improvements)
 * @author Fabien Maussion (java translation)
 */
class SGP4 extends TLEPropagator {

    /** 
     * If perige is less than 220 km, some calculus are avoided.
     */
    private boolean lessThan220;
    /**
     * (1 + eta * cos(M0))<sup>3</sup>.
     */
    private double delM0;

    private double d2;
    private double d3;
    private double d4;
    private double t3cof;
    private double t4cof;
    private double t5cof;
    private double sinM0;
    private double omgcof;
    private double xmcof;
    private double c5;

    /** 
     * @param initialTLE The unique TLE to propagate
     * @exception OrekitException Some specific error occurs
     */
    SGP4(final TLE initialTLE) throws OrekitException {
        super(initialTLE);
    }


    /** 
     * Initialization proper to each propagator (SGP or SDP).
     */
    protected void sxpInitialize() {
        /*
         * For perigee less than 220 kilometers, the equations are
         * truncated to linear variation in sqrt a and quadratic
         * variation in mean anomaly.  Also the c3 term, the delta
         * omega term, and the delta m term are dropped.
        */
        this.lessThan220 = (this.perige < 220);
        if (!this.lessThan220) {
            final double c1sq = (c1 * c1);
            this.delM0 = (1.0 + (eta * Math.cos(tle.getMeanAnomaly())));
            this.delM0 *= (this.delM0 * this.delM0);
            this.d2 = (4 * a0dp * tsi * c1sq);
            final double temp = ((d2 * tsi * c1) / 3.0);
            this.d3 = ((17 * a0dp + s4) * temp);
            this.d4 = (0.5 * temp * a0dp * tsi * (221 * a0dp + 31 * s4) * c1);
            this.t3cof = (d2 + (2 * c1sq));
            this.t4cof = (0.25 * (3 * d3 + c1 * (12 * d2 + 10 * c1sq)));
            this.t5cof = (0.2 * (3 * d4 + 12 * c1 * d3 + 6 * d2 * d2 + 15 * c1sq * (2 * d2 + c1sq)));
            this.sinM0 = Math.sin(tle.getMeanAnomaly());
            if (tle.getE() < 1e-4) {
                this.omgcof = 0.0;
                this.xmcof = 0.0;
            }
            else  {
                final double c3 = (coef * tsi * TLEConstants.A3OVK2 * xn0dp *
                                   TLEConstants.NORMALIZED_EQUATORIAL_RADIUS * sini0 / tle.getE());
                this.xmcof = (-TLEConstants.TWO_THIRD * coef * tle.getBStar() *
                              TLEConstants.NORMALIZED_EQUATORIAL_RADIUS / eeta);
                this.omgcof = (tle.getBStar() * c3 * Math.cos(tle.getPerigeeArgument()));
            }
        }
        this.c5 = (2 * coef1 * a0dp * beta02 * (1 + 2.75 * (etasq + eeta) + eeta * etasq));
    }
    /** 
     * Propagation proper to each propagator (SGP or SDP).
     * 
     * @param tSince the offset from initial epoch (min)
     */
    protected void sxpPropagate(final double tSince) {
        /*
         * Update for secular gravity and atmospheric drag.
         */
        final double xmdf = (tle.getMeanAnomaly() + xmdot * tSince);
        final double omgadf = (tle.getPerigeeArgument() + omgdot * tSince);
        final double xn0ddf = (tle.getRaan() + xnodot * tSince);
        this.omega = omgadf;
        double xmp = xmdf;
        final double tsq = tSince * tSince;
        this.xnode = xn0ddf + xnodcf * tsq;
        double tempa = 1 - c1 * tSince;
        double tempe = tle.getBStar() * c4 * tSince;
        double templ = t2cof * tsq;

        if (!this.lessThan220) {
            final double delomg = omgcof * tSince;
            double delm = 1. + eta * Math.cos(xmdf);
            delm = xmcof * (delm * delm * delm - delM0);
            final double temp = delomg + delm;
            xmp = xmdf + temp;
            omega = omgadf - temp;
            final double tcube = tsq * tSince;
            final double tfour = tSince * tcube;
            tempa = tempa - d2 * tsq - d3 * tcube - d4 * tfour;
            tempe = tempe + tle.getBStar() * c5 * (Math.sin(xmp) - sinM0);
            templ = templ + t3cof * tcube + tfour * (t4cof + tSince * t5cof);
        }

        this.a = a0dp * tempa * tempa;
        this.e = tle.getE() - tempe;
        /*
         * A highly arbitrary lower limit on e,  of 1e-6:
         */
        if (e < 1e-6) {
            e = 1e-6;
        }

        this.xl = (xmp + omega + xnode + xn0dp * templ);

        this.i = tle.getI();
    }
}
